1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.codehaus.groovy.runtime;
20
21 import groovy.lang.Closure;
22 import groovy.lang.GeneratedGroovyProxy;
23 import groovy.lang.GroovyClassLoader;
24 import groovy.lang.GroovyObject;
25 import groovy.lang.GroovyRuntimeException;
26 import groovy.transform.Trait;
27 import org.codehaus.groovy.ast.ClassHelper;
28 import org.codehaus.groovy.ast.ClassNode;
29 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
30 import org.codehaus.groovy.control.CompilationUnit;
31 import org.codehaus.groovy.control.CompilerConfiguration;
32 import org.codehaus.groovy.control.ErrorCollector;
33 import org.codehaus.groovy.control.Phases;
34 import org.codehaus.groovy.control.SourceUnit;
35 import org.codehaus.groovy.tools.GroovyClass;
36 import org.codehaus.groovy.transform.trait.Traits;
37 import org.objectweb.asm.ClassVisitor;
38 import org.objectweb.asm.ClassWriter;
39 import org.objectweb.asm.Label;
40 import org.objectweb.asm.MethodVisitor;
41 import org.objectweb.asm.Opcodes;
42 import org.objectweb.asm.Type;
43
44 import java.lang.annotation.Annotation;
45 import java.lang.reflect.Constructor;
46 import java.lang.reflect.InvocationTargetException;
47 import java.lang.reflect.Method;
48 import java.lang.reflect.Modifier;
49 import java.security.AccessController;
50 import java.security.PrivilegedAction;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.HashSet;
57 import java.util.LinkedHashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.concurrent.atomic.AtomicLong;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
89 private static final Map<String, Boolean> EMPTY_DELEGATECLOSURE_MAP = Collections.emptyMap();
90 private static final Set<String> EMPTY_STRING_SET = Collections.emptySet();
91
92 private static final String CLOSURES_MAP_FIELD = "$closures$delegate$map";
93 private static final String DELEGATE_OBJECT_FIELD = "$delegate";
94 private static List<Method> OBJECT_METHODS = getInheritedMethods(Object.class, new ArrayList<Method>());
95 private static List<Method> GROOVYOBJECT_METHODS = getInheritedMethods(GroovyObject.class, new ArrayList<Method>());
96
97 private static final AtomicLong pxyCounter = new AtomicLong();
98 private static final Set<String> GROOVYOBJECT_METHOD_NAMESS;
99 private static final Object[] EMPTY_ARGS = new Object[0];
100
101 static {
102 List<String> names = new ArrayList<String>();
103 for (Method method : GroovyObject.class.getMethods()) {
104 names.add(method.getName());
105 }
106 GROOVYOBJECT_METHOD_NAMESS = new HashSet<String>(names);
107 }
108
109 private final Class superClass;
110 private final Class delegateClass;
111 private final InnerLoader loader;
112 private final String proxyName;
113 private final LinkedHashSet<Class> classList;
114 private final Map<String, Boolean> delegatedClosures;
115
116
117 private final boolean emptyBody;
118 private final boolean hasWildcard;
119 private final boolean generateDelegateField;
120 private final Set<String> objectDelegateMethods;
121
122 private final Set<Object> visitedMethods;
123
124
125 private final Class cachedClass;
126 private final Constructor cachedNoArgConstructor;
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public ProxyGeneratorAdapter(
141 final Map<Object, Object> closureMap,
142 final Class superClass,
143 final Class[] interfaces,
144 final ClassLoader proxyLoader,
145 final boolean emptyBody,
146 final Class delegateClass) {
147 super(Opcodes.ASM4, new ClassWriter(0));
148 this.loader = proxyLoader!=null?createInnerLoader(proxyLoader):findClassLoader(superClass);
149 this.visitedMethods = new LinkedHashSet<Object>();
150 this.delegatedClosures = closureMap.isEmpty()? EMPTY_DELEGATECLOSURE_MAP :new HashMap<String, Boolean>();
151 boolean wildcard = false;
152 for (Map.Entry<Object, Object> entry : closureMap.entrySet()) {
153 String name = entry.getKey().toString();
154 if ("*".equals(name)) {
155 wildcard = true;
156 }
157 this.delegatedClosures.put(name, Boolean.FALSE);
158 }
159 this.hasWildcard = wildcard;
160
161 Class fixedSuperClass = adjustSuperClass(superClass, interfaces);
162
163
164 this.generateDelegateField = delegateClass!=null;
165 this.objectDelegateMethods = generateDelegateField?createDelegateMethodList(fixedSuperClass, delegateClass, interfaces):EMPTY_STRING_SET;
166 this.delegateClass = delegateClass;
167
168
169
170
171 this.superClass = fixedSuperClass;
172
173
174 this.classList = new LinkedHashSet<Class>();
175 this.classList.add(superClass);
176 if (generateDelegateField) {
177 classList.add(delegateClass);
178 Collections.addAll(this.classList, delegateClass.getInterfaces());
179 }
180 if (interfaces!=null) {
181 Collections.addAll(this.classList, interfaces);
182 }
183 this.proxyName = proxyName();
184 this.emptyBody = emptyBody;
185
186
187 ClassWriter writer = (ClassWriter) cv;
188 this.visit(Opcodes.V1_5, ACC_PUBLIC, proxyName, null, null, null);
189 byte[] b = writer.toByteArray();
190
191 cachedClass = loader.defineClass(proxyName.replace('/','.'), b);
192
193 Class[] args = generateDelegateField?new Class[] { Map.class, delegateClass }:new Class[] { Map.class };
194 Constructor constructor;
195 try {
196 constructor = cachedClass.getConstructor(args);
197 } catch (NoSuchMethodException e) {
198 constructor = null;
199 }
200 cachedNoArgConstructor = constructor;
201 }
202
203 private Class adjustSuperClass(Class superClass, final Class[] interfaces) {
204 boolean isSuperClassAnInterface = superClass.isInterface();
205 if (!isSuperClassAnInterface) {
206 return superClass;
207 }
208 Class result = Object.class;
209 Set<ClassNode> traits = new LinkedHashSet<ClassNode>();
210
211 collectTraits(superClass, traits);
212 if (interfaces!=null) {
213 for (Class anInterface : interfaces) {
214 collectTraits(anInterface, traits);
215 }
216 }
217 if (!traits.isEmpty()) {
218 String name = superClass.getName() + "$TraitAdapter";
219 ClassNode cn = new ClassNode(name, ACC_PUBLIC | ACC_ABSTRACT, ClassHelper.OBJECT_TYPE, traits.toArray(new ClassNode[traits.size()]), null);
220 CompilationUnit cu = new CompilationUnit(loader);
221 CompilerConfiguration config = new CompilerConfiguration();
222 SourceUnit su = new SourceUnit(name+"wrapper", "", config, loader, new ErrorCollector(config));
223 cu.addSource(su);
224 cu.compile(Phases.CONVERSION);
225 su.getAST().addClass(cn);
226 cu.compile(Phases.CLASS_GENERATION);
227 @SuppressWarnings("unchecked")
228 List<GroovyClass> classes = (List<GroovyClass>) cu.getClasses();
229 for (GroovyClass groovyClass : classes) {
230 if (groovyClass.getName().equals(name)) {
231 return loader.defineClass(name,groovyClass.getBytes());
232 }
233 }
234 }
235 return result;
236 }
237
238 private void collectTraits(final Class clazz, final Set<ClassNode> traits) {
239 Annotation annotation = clazz.getAnnotation(Trait.class);
240 if (annotation!=null) {
241 ClassNode trait = ClassHelper.make(clazz);
242 traits.add(trait.getPlainNodeReference());
243 LinkedHashSet<ClassNode> selfTypes = new LinkedHashSet<ClassNode>();
244 Traits.collectSelfTypes(trait, selfTypes, true, true);
245 for (ClassNode selfType : selfTypes) {
246 if (Traits.isTrait(selfType)) {
247 traits.add(selfType.getPlainNodeReference());
248 }
249 }
250 }
251 }
252
253 private static InnerLoader createInnerLoader(final ClassLoader parent) {
254 return AccessController.doPrivileged(new PrivilegedAction<InnerLoader>() {
255 public InnerLoader run() {
256 return new InnerLoader(parent);
257 }
258 });
259 }
260
261 private InnerLoader findClassLoader(Class clazz) {
262 ClassLoader cl = clazz.getClassLoader();
263 if (cl==null) cl = this.getClass().getClassLoader();
264 return createInnerLoader(cl);
265 }
266
267 private static Set<String> createDelegateMethodList(Class superClass, Class delegateClass, Class[] interfaces) {
268 Set<String> selectedMethods = new HashSet<String>();
269 List<Method> interfaceMethods = new ArrayList<Method>();
270 List<Method> superClassMethods = new ArrayList<Method>();
271 Collections.addAll(superClassMethods, superClass.getDeclaredMethods());
272 if (interfaces!=null) {
273 for (Class thisInterface : interfaces) {
274 getInheritedMethods(thisInterface, interfaceMethods);
275 }
276 for (Method method : interfaceMethods) {
277 if (!(containsEquivalentMethod(superClassMethods, method))) {
278 selectedMethods.add(method.getName()+Type.getMethodDescriptor(method));
279 }
280 }
281 }
282 List<Method> additionalMethods = getInheritedMethods(delegateClass, new ArrayList<Method>());
283 for (Method method : additionalMethods) {
284 if (method.getName().indexOf('$') != -1)
285 continue;
286 if (!containsEquivalentMethod(interfaceMethods, method) &&
287 !containsEquivalentMethod(OBJECT_METHODS, method) &&
288 !containsEquivalentMethod(GROOVYOBJECT_METHODS, method)) {
289 selectedMethods.add(method.getName()+Type.getMethodDescriptor(method));
290 }
291 }
292 return selectedMethods;
293 }
294
295 private static List<Method> getInheritedMethods(Class baseClass, List<Method> methods) {
296 Collections.addAll(methods, baseClass.getMethods());
297 Class currentClass = baseClass;
298 while (currentClass != null) {
299 Method[] protectedMethods = currentClass.getDeclaredMethods();
300 for (Method method : protectedMethods) {
301 if (method.getName().indexOf('$') != -1)
302 continue;
303 if (Modifier.isProtected(method.getModifiers()) && !containsEquivalentMethod(methods, method))
304 methods.add(method);
305 }
306 currentClass = currentClass.getSuperclass();
307 }
308 return methods;
309 }
310
311 private static boolean containsEquivalentMethod(Collection<Method> publicAndProtectedMethods, Method candidate) {
312 for (Method method : publicAndProtectedMethods) {
313 if (candidate.getName().equals(method.getName()) &&
314 candidate.getReturnType().equals(method.getReturnType()) &&
315 hasMatchingParameterTypes(candidate, method)) {
316 return true;
317 }
318 }
319 return false;
320 }
321
322 private static boolean hasMatchingParameterTypes(Method method, Method candidate) {
323 Class[] candidateParamTypes = candidate.getParameterTypes();
324 Class[] methodParamTypes = method.getParameterTypes();
325 if (candidateParamTypes.length != methodParamTypes.length) return false;
326 for (int i = 0; i < methodParamTypes.length; i++) {
327 if (!candidateParamTypes[i].equals(methodParamTypes[i])) return false;
328 }
329 return true;
330 }
331
332 @Override
333 public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
334 Set<String> interfacesSet = new LinkedHashSet<String>();
335 if (interfaces != null) Collections.addAll(interfacesSet, interfaces);
336 for (Class extraInterface : classList) {
337 if (extraInterface.isInterface()) interfacesSet.add(BytecodeHelper.getClassInternalName(extraInterface));
338 }
339 final boolean addGroovyObjectSupport = !GroovyObject.class.isAssignableFrom(superClass);
340 if (addGroovyObjectSupport) interfacesSet.add("groovy/lang/GroovyObject");
341 if (generateDelegateField) {
342 classList.add(GeneratedGroovyProxy.class);
343 interfacesSet.add("groovy/lang/GeneratedGroovyProxy");
344 }
345 super.visit(V1_5, ACC_PUBLIC, proxyName, signature, BytecodeHelper.getClassInternalName(superClass), interfacesSet.toArray(new String[interfacesSet.size()]));
346 visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
347 addDelegateFields();
348 if (addGroovyObjectSupport) {
349 createGroovyObjectSupport();
350 }
351 for (Class clazz : classList) {
352 visitClass(clazz);
353 }
354 }
355
356
357
358
359
360
361 private void visitClass(final Class clazz) {
362 Method[] methods = clazz.getDeclaredMethods();
363 for (Method method : methods) {
364 Class<?>[] exceptionTypes = method.getExceptionTypes();
365 String[] exceptions = new String[exceptionTypes.length];
366 for (int i = 0; i < exceptions.length; i++) {
367 exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]);
368 }
369
370 visitMethod(method.getModifiers(),
371 method.getName(),
372 BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes()),
373 null,
374 exceptions);
375 }
376 Constructor[] constructors = clazz.getDeclaredConstructors();
377 for (Constructor method : constructors) {
378 Class<?>[] exceptionTypes = method.getExceptionTypes();
379 String[] exceptions = new String[exceptionTypes.length];
380 for (int i = 0; i < exceptions.length; i++) {
381 exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]);
382 }
383
384 visitMethod(method.getModifiers(),
385 "<init>",
386 BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()),
387 null,
388 exceptions);
389 }
390
391 for (Class intf : clazz.getInterfaces()) {
392 visitClass(intf);
393 }
394 Class superclass = clazz.getSuperclass();
395 if (superclass!=null) visitClass(superclass);
396
397
398
399 for (Map.Entry<String, Boolean> entry : delegatedClosures.entrySet()) {
400 Boolean visited = entry.getValue();
401 if (!visited) {
402 String name = entry.getKey();
403 if (!"*".equals(name)) {
404
405 visitMethod(ACC_PUBLIC, name, "([Ljava/lang/Object;)Ljava/lang/Object;", null, null);
406 }
407 }
408 }
409 }
410
411
412
413
414
415 private void createGroovyObjectSupport() {
416 visitField(ACC_PRIVATE + ACC_TRANSIENT, "metaClass", "Lgroovy/lang/MetaClass;", null, null);
417
418
419 MethodVisitor mv;
420 {
421 mv = super.visitMethod(ACC_PUBLIC, "getMetaClass", "()Lgroovy/lang/MetaClass;", null, null);
422 mv.visitCode();
423 Label l0 = new Label();
424 mv.visitLabel(l0);
425 mv.visitVarInsn(ALOAD, 0);
426 mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
427 Label l1 = new Label();
428 mv.visitJumpInsn(IFNONNULL, l1);
429 Label l2 = new Label();
430 mv.visitLabel(l2);
431 mv.visitVarInsn(ALOAD, 0);
432 mv.visitVarInsn(ALOAD, 0);
433 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
434 mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "getMetaClass", "(Ljava/lang/Class;)Lgroovy/lang/MetaClass;", false);
435 mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
436 mv.visitLabel(l1);
437 mv.visitVarInsn(ALOAD, 0);
438 mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
439 mv.visitInsn(ARETURN);
440 mv.visitMaxs(2, 1);
441 mv.visitEnd();
442 }
443
444
445 {
446 mv = super.visitMethod(ACC_PUBLIC, "getProperty", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);
447 mv.visitCode();
448 mv.visitIntInsn(ALOAD, 0);
449 mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/GroovyObject", "getMetaClass", "()Lgroovy/lang/MetaClass;", true);
450 mv.visitIntInsn(ALOAD, 0);
451 mv.visitVarInsn(ALOAD, 1);
452 mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "getProperty", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", true);
453 mv.visitInsn(ARETURN);
454 mv.visitMaxs(3, 2);
455 mv.visitEnd();
456 }
457
458
459 {
460 mv = super.visitMethod(ACC_PUBLIC, "setProperty", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null);
461 mv.visitCode();
462 Label l0 = new Label();
463 mv.visitLabel(l0);
464 mv.visitVarInsn(ALOAD, 0);
465 mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
466 mv.visitVarInsn(ALOAD, 0);
467 mv.visitVarInsn(ALOAD, 1);
468 mv.visitVarInsn(ALOAD, 2);
469 mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "setProperty", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V", true);
470 Label l1 = new Label();
471 mv.visitLabel(l1);
472 mv.visitInsn(RETURN);
473 Label l2 = new Label();
474 mv.visitLabel(l2);
475 mv.visitMaxs(4, 3);
476 mv.visitEnd();
477 }
478
479
480 {
481 mv = super.visitMethod(ACC_PUBLIC, "invokeMethod", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", null, null);
482 Label l0 = new Label();
483 mv.visitLabel(l0);
484 mv.visitVarInsn(ALOAD, 0);
485 mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
486 mv.visitVarInsn(ALOAD, 0);
487 mv.visitVarInsn(ALOAD, 1);
488 mv.visitVarInsn(ALOAD, 2);
489 mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", true);
490 mv.visitInsn(ARETURN);
491 Label l1 = new Label();
492 mv.visitLabel(l1);
493 mv.visitMaxs(4, 3);
494 mv.visitEnd();
495 }
496
497
498 {
499 mv = super.visitMethod(ACC_PUBLIC, "setMetaClass", "(Lgroovy/lang/MetaClass;)V", null, null);
500 mv.visitCode();
501 Label l0 = new Label();
502 mv.visitLabel(l0);
503 mv.visitVarInsn(ALOAD, 0);
504 mv.visitVarInsn(ALOAD, 1);
505 mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
506 Label l1 = new Label();
507 mv.visitLabel(l1);
508 mv.visitInsn(RETURN);
509 Label l2 = new Label();
510 mv.visitLabel(l2);
511 mv.visitMaxs(2, 2);
512 mv.visitEnd();
513 }
514
515 }
516
517
518
519
520 private void addDelegateFields() {
521 visitField(ACC_PRIVATE + ACC_FINAL, CLOSURES_MAP_FIELD, "Ljava/util/Map;", null, null);
522 if (generateDelegateField) {
523 visitField(ACC_PRIVATE+ACC_FINAL, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass), null, null);
524 }
525 }
526
527 private String proxyName() {
528 String name = delegateClass!=null?delegateClass.getName():superClass.getName();
529 int index = name.lastIndexOf('.');
530 if (index == -1) return name + pxyCounter.incrementAndGet() + "_groovyProxy";
531 return name.substring(index + 1, name.length()) + pxyCounter.incrementAndGet() + "_groovyProxy";
532 }
533
534 private static boolean isImplemented(Class clazz, String name, String desc) {
535 Method[] methods = clazz.getDeclaredMethods();
536 for (Method method : methods) {
537 if (method.getName().equals(name)) {
538 if (desc.equals(Type.getMethodDescriptor(method))) {
539 return !Modifier.isAbstract(method.getModifiers());
540 }
541 }
542 }
543 Class parent = clazz.getSuperclass();
544 if (parent !=null) {
545 return isImplemented(parent, name, desc);
546 }
547 return false;
548 }
549
550 @Override
551 public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
552 Object key = Arrays.asList(name, desc);
553 if (visitedMethods.contains(key)) return null;
554 if (Modifier.isPrivate(access) || Modifier.isNative(access) || ((access&ACC_SYNTHETIC)!=0)) {
555
556 return null;
557 }
558 int accessFlags = access;
559 visitedMethods.add(key);
560 if ((objectDelegateMethods.contains(name+desc) || delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) && !Modifier.isStatic(access) && !Modifier.isFinal(access)) {
561 if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
562 if (Modifier.isAbstract(access)) {
563
564 accessFlags -= ACC_ABSTRACT;
565 }
566 if (delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) {
567 delegatedClosures.put(name, Boolean.TRUE);
568 return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
569 }
570 if (generateDelegateField && objectDelegateMethods.contains(name+desc)) {
571 return makeDelegateCall(name, desc, signature, exceptions, accessFlags);
572 }
573 delegatedClosures.put(name, Boolean.TRUE);
574 return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
575 }
576 } else if ("getProxyTarget".equals(name) && "()Ljava/lang/Object;".equals(desc)) {
577 return createGetProxyTargetMethod(access, name, desc, signature, exceptions);
578 } else if ("<init>".equals(name) && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
579 return createConstructor(access, name, desc, signature, exceptions);
580 } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
581 if (isImplemented(superClass, name, desc)) {
582 return null;
583 }
584 accessFlags -= ACC_ABSTRACT;
585 MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
586 mv.visitCode();
587 Type[] args = Type.getArgumentTypes(desc);
588 if (emptyBody) {
589 Type returnType = Type.getReturnType(desc);
590 if (returnType==Type.VOID_TYPE) {
591 mv.visitInsn(RETURN);
592 } else {
593 int loadIns = getLoadInsn(returnType);
594 switch (loadIns) {
595 case ILOAD: mv.visitInsn(ICONST_0);
596 break;
597 case LLOAD: mv.visitInsn(LCONST_0);
598 break;
599 case FLOAD: mv.visitInsn(FCONST_0);
600 break;
601 case DLOAD: mv.visitInsn(DCONST_0);
602 break;
603 default:
604 mv.visitInsn(ACONST_NULL);
605 }
606 mv.visitInsn(getReturnInsn(returnType));
607 mv.visitMaxs(2, registerLen(args)+1);
608 }
609 } else {
610
611
612 mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
613 mv.visitInsn(DUP);
614 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "()V", false);
615 mv.visitInsn(ATHROW);
616 mv.visitMaxs(2, registerLen(args)+1);
617 }
618 mv.visitEnd();
619 }
620 return null;
621 }
622
623 private MethodVisitor createGetProxyTargetMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
624 MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_FINAL, name, desc, signature, exceptions);
625 mv.visitCode();
626 mv.visitIntInsn(ALOAD,0);
627 mv.visitFieldInsn(GETFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass));
628 mv.visitInsn(ARETURN);
629 mv.visitMaxs(1,1);
630 mv.visitEnd();
631 return null;
632 }
633
634 private int registerLen(Type[] args) {
635 int i = 0;
636 for (Type arg : args) {
637 i += registerLen(arg);
638 }
639 return i;
640 }
641
642 private int registerLen(final Type arg) {
643 return arg== Type.DOUBLE_TYPE||arg==Type.LONG_TYPE?2:1;
644 }
645
646 private MethodVisitor createConstructor(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
647 Type[] args = Type.getArgumentTypes(desc);
648 StringBuilder newDesc = new StringBuilder("(");
649 for (Type arg : args) {
650 newDesc.append(arg.getDescriptor());
651 }
652 newDesc.append("Ljava/util/Map;");
653 if (generateDelegateField) {
654 newDesc.append(BytecodeHelper.getTypeDescription(delegateClass));
655 }
656 newDesc.append(")V");
657 MethodVisitor mv = super.visitMethod(access, name, newDesc.toString(), signature, exceptions);
658 mv.visitCode();
659 initializeDelegateClosure(mv, args);
660 if (generateDelegateField) {
661 initializeDelegateObject(mv, args);
662 }
663 mv.visitVarInsn(ALOAD, 0);
664 int idx = 1;
665 for (Type arg : args) {
666 if (isPrimitive(arg)) {
667 mv.visitIntInsn(getLoadInsn(arg), idx);
668 } else {
669 mv.visitVarInsn(ALOAD, idx);
670 }
671 idx += registerLen(arg);
672 }
673 mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superClass), "<init>", desc, false);
674 mv.visitInsn(RETURN);
675 int max = idx + 1 + (generateDelegateField?1:0);
676 mv.visitMaxs(max, max);
677 mv.visitEnd();
678 return null;
679 }
680
681 private void initializeDelegateClosure(final MethodVisitor mv, Type[] args) {
682 int idx = 1 + getTypeArgsRegisterLength(args);
683
684 mv.visitIntInsn(ALOAD, 0);
685 mv.visitIntInsn(ALOAD, idx);
686
687 mv.visitFieldInsn(PUTFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;");
688 }
689
690 private void initializeDelegateObject(final MethodVisitor mv, Type[] args) {
691 int idx = 2 + getTypeArgsRegisterLength(args);
692
693 mv.visitIntInsn(ALOAD, 0);
694 mv.visitIntInsn(ALOAD, idx);
695 mv.visitFieldInsn(PUTFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass));
696 }
697
698 private int getTypeArgsRegisterLength(Type[] args) {
699 int length = 0;
700 for (Type type : args) { length += registerLen(type); }
701 return length;
702 }
703
704
705
706
707 protected MethodVisitor makeDelegateCall(final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags) {
708 MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
709 mv.visitVarInsn(ALOAD, 0);
710 mv.visitFieldInsn(GETFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass));
711
712 int size;
713 mv.visitLdcInsn(name);
714 Type[] args = Type.getArgumentTypes(desc);
715 BytecodeHelper.pushConstant(mv, args.length);
716 mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
717 size = 6;
718 int idx = 1;
719 for (int i = 0; i < args.length; i++) {
720 Type arg = args[i];
721 mv.visitInsn(DUP);
722 BytecodeHelper.pushConstant(mv, i);
723
724 if (isPrimitive(arg)) {
725 mv.visitIntInsn(getLoadInsn(arg), idx);
726 String wrappedType = getWrappedClassDescriptor(arg);
727 mv.visitMethodInsn(INVOKESTATIC, wrappedType, "valueOf", "(" + arg.getDescriptor() + ")L" + wrappedType + ";", false);
728 } else {
729 mv.visitVarInsn(ALOAD, idx);
730 }
731 size = Math.max(size, 5+registerLen(arg));
732 idx += registerLen(arg);
733 mv.visitInsn(AASTORE);
734 }
735 mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", false);
736 unwrapResult(mv, desc);
737 mv.visitMaxs(size, registerLen(args) + 1);
738
739 return mv;
740 }
741
742 protected MethodVisitor makeDelegateToClosureCall(final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags) {
743 MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
744
745
746 mv.visitCode();
747 int stackSize = 0;
748
749
750 Type[] args = Type.getArgumentTypes(desc);
751 int arrayStore = args.length+1;
752 BytecodeHelper.pushConstant(mv, args.length);
753 mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
754 stackSize = 1;
755 int idx = 1;
756 for (int i = 0; i < args.length; i++) {
757 Type arg = args[i];
758 mv.visitInsn(DUP);
759 BytecodeHelper.pushConstant(mv, i);
760 stackSize = 3;
761
762 if (isPrimitive(arg)) {
763 mv.visitIntInsn(getLoadInsn(arg), idx);
764 String wrappedType = getWrappedClassDescriptor(arg);
765 mv.visitMethodInsn(INVOKESTATIC, wrappedType, "valueOf", "(" + arg.getDescriptor() + ")L" + wrappedType + ";", false);
766 } else {
767 mv.visitVarInsn(ALOAD, idx);
768 }
769 idx += registerLen(arg);
770 stackSize = Math.max(4, 3+registerLen(arg));
771 mv.visitInsn(AASTORE);
772 }
773 mv.visitVarInsn(ASTORE, arrayStore);
774 int arrayIndex = arrayStore;
775 mv.visitVarInsn(ALOAD, 0);
776 mv.visitFieldInsn(GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;");
777 mv.visitLdcInsn(name);
778 mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
779 arrayStore++;
780 mv.visitVarInsn(ASTORE, arrayStore);
781
782 Label notNull = new Label();
783 mv.visitIntInsn(ALOAD, arrayStore);
784 mv.visitJumpInsn(IFNONNULL, notNull);
785 mv.visitVarInsn(ALOAD, 0);
786 mv.visitFieldInsn(GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;");
787 mv.visitLdcInsn("*");
788 mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
789 mv.visitVarInsn(ASTORE, arrayStore);
790 mv.visitLabel(notNull);
791 mv.visitVarInsn(ALOAD, arrayStore);
792 mv.visitMethodInsn(INVOKESTATIC, BytecodeHelper.getClassInternalName(this.getClass()), "ensureClosure", "(Ljava/lang/Object;)Lgroovy/lang/Closure;", false);
793 mv.visitVarInsn(ALOAD, arrayIndex);
794 stackSize++;
795 mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure", "call", "([Ljava/lang/Object;)Ljava/lang/Object;", false);
796 unwrapResult(mv, desc);
797 mv.visitMaxs(stackSize, arrayStore+1);
798 mv.visitEnd();
799
800 return null;
801 }
802
803 private void unwrapResult(final MethodVisitor mv, final String desc) {
804 Type returnType = Type.getReturnType(desc);
805 if (returnType==Type.VOID_TYPE) {
806 mv.visitInsn(POP);
807 mv.visitInsn(RETURN);
808 } else {
809 if (isPrimitive(returnType)) {
810 BytecodeHelper.unbox(mv, ClassHelper.make(returnType.getClassName()));
811 } else {
812 mv.visitTypeInsn(CHECKCAST, returnType.getInternalName());
813 }
814 mv.visitInsn(getReturnInsn(returnType));
815 }
816 }
817
818 @SuppressWarnings("unchecked")
819 public GroovyObject proxy(Map<Object,Object> map, Object... constructorArgs) {
820 if (constructorArgs==null && cachedNoArgConstructor!=null) {
821
822 try {
823 return (GroovyObject) cachedNoArgConstructor.newInstance(map);
824 } catch (InstantiationException e) {
825 throw new GroovyRuntimeException(e);
826 } catch (IllegalAccessException e) {
827 throw new GroovyRuntimeException(e);
828 } catch (InvocationTargetException e) {
829 throw new GroovyRuntimeException(e);
830 }
831 }
832 if (constructorArgs==null) constructorArgs= EMPTY_ARGS;
833 Object[] values = new Object[constructorArgs.length + 1];
834 System.arraycopy(constructorArgs, 0, values, 0, constructorArgs.length);
835 values[values.length-1] = map;
836 return DefaultGroovyMethods.<GroovyObject>newInstance(cachedClass, values);
837 }
838
839 @SuppressWarnings("unchecked")
840 public GroovyObject delegatingProxy(Object delegate,Map<Object,Object> map, Object... constructorArgs) {
841 if (constructorArgs==null && cachedNoArgConstructor!=null) {
842
843 try {
844 return (GroovyObject) cachedNoArgConstructor.newInstance(map, delegate);
845 } catch (InstantiationException e) {
846 throw new GroovyRuntimeException(e);
847 } catch (IllegalAccessException e) {
848 throw new GroovyRuntimeException(e);
849 } catch (InvocationTargetException e) {
850 throw new GroovyRuntimeException(e);
851 }
852 }
853 if (constructorArgs==null) constructorArgs= EMPTY_ARGS;
854 Object[] values = new Object[constructorArgs.length + 2];
855 System.arraycopy(constructorArgs, 0, values, 0, constructorArgs.length);
856 values[values.length-2] = map;
857 values[values.length-1] = delegate;
858 return DefaultGroovyMethods.<GroovyObject>newInstance(cachedClass, values);
859 }
860
861
862
863
864
865
866 @SuppressWarnings("unchecked")
867 public static Closure ensureClosure(Object o) {
868 if (o==null) throw new UnsupportedOperationException();
869 if (o instanceof Closure) return (Closure) o;
870 return new ReturnValueWrappingClosure(o);
871 }
872
873 private static int getLoadInsn(final Type type) {
874 if (type == Type.BOOLEAN_TYPE) return ILOAD;
875 if (type == Type.BYTE_TYPE) return ILOAD;
876 if (type == Type.CHAR_TYPE) return ILOAD;
877 if (type == Type.DOUBLE_TYPE) return DLOAD;
878 if (type == Type.FLOAT_TYPE) return FLOAD;
879 if (type == Type.INT_TYPE) return ILOAD;
880 if (type == Type.LONG_TYPE) return LLOAD;
881 if (type == Type.SHORT_TYPE) return ILOAD;
882 return ALOAD;
883 }
884
885 private static int getReturnInsn(final Type type) {
886 if (type == Type.BOOLEAN_TYPE) return IRETURN;
887 if (type == Type.BYTE_TYPE) return IRETURN;
888 if (type == Type.CHAR_TYPE) return IRETURN;
889 if (type == Type.DOUBLE_TYPE) return DRETURN;
890 if (type == Type.FLOAT_TYPE) return FRETURN;
891 if (type == Type.INT_TYPE) return IRETURN;
892 if (type == Type.LONG_TYPE) return LRETURN;
893 if (type == Type.SHORT_TYPE) return IRETURN;
894 return ARETURN;
895 }
896
897 private boolean isPrimitive(final Type arg) {
898 return arg == Type.BOOLEAN_TYPE
899 || arg == Type.BYTE_TYPE
900 || arg == Type.CHAR_TYPE
901 || arg == Type.DOUBLE_TYPE
902 || arg == Type.FLOAT_TYPE
903 || arg == Type.INT_TYPE
904 || arg == Type.LONG_TYPE
905 || arg == Type.SHORT_TYPE;
906 }
907
908 private String getWrappedClassDescriptor(Type type) {
909 if (type == Type.BOOLEAN_TYPE) return "java/lang/Boolean";
910 if (type == Type.BYTE_TYPE) return "java/lang/Byte";
911 if (type == Type.CHAR_TYPE) return "java/lang/Character";
912 if (type == Type.DOUBLE_TYPE) return "java/lang/Double";
913 if (type == Type.FLOAT_TYPE) return "java/lang/Float";
914 if (type == Type.INT_TYPE) return "java/lang/Integer";
915 if (type == Type.LONG_TYPE) return "java/lang/Long";
916 if (type == Type.SHORT_TYPE) return "java/lang/Short";
917 throw new IllegalArgumentException("Unexpected type class [" + type + "]");
918 }
919
920 private static class InnerLoader extends GroovyClassLoader {
921 protected InnerLoader(final ClassLoader parent) {
922 super(parent);
923 }
924
925 public Class defineClass(String name, byte[] data) {
926 return super.defineClass(name, data, 0, data.length);
927 }
928
929 }
930
931 private static class ReturnValueWrappingClosure<V> extends Closure<V>{
932 private final V value;
933
934 public ReturnValueWrappingClosure(V returnValue) {
935 super(null);
936 value = returnValue;
937 }
938
939 @Override
940 public V call(final Object... args) {
941 return value;
942 }
943 }
944
945 }